#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <memory>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <unistd.h>

const static int default_backlog = 8;

enum
{
    Usage_Err = 1,
    Socket_Err,
    Bind_Err,
    Listen_Err
};

#define CONV(addr_ptr) ((struct sockaddr *)addr_ptr)

class TcpServer
{
public:
    TcpServer(uint16_t port) : _port(port), _isrunning(false)
    {
    }
    // 都是固定套路
    void Init()
    {
        // 1. 创建socket, file fd, 本质是文件
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            exit(0);
        }
        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));

        // 2. 填充本地网络信息并bind
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY);

        // 2.1 bind
        if (bind(_listensock, CONV(&local), sizeof(local)) != 0)
        {
            exit(Bind_Err);
        }

        // 3. 设置socket为监听状态，tcp特有的
        if (listen(_listensock, default_backlog) != 0)
        {
            exit(Listen_Err);
        }
    }
    void ProcessConnection(int sockfd, struct sockaddr_in &peer)
    {
        uint16_t clientport = ntohs(peer.sin_port);
        std::string clientip = inet_ntoa(peer.sin_addr);
        std::string prefix = clientip + ":" + std::to_string(clientport);
        std::cout << "get a new connection, info is : " << prefix << std::endl;
        while (true)
        {
            char inbuffer[1024];
            ssize_t s = ::read(sockfd, inbuffer, sizeof(inbuffer)-1);
            if(s > 0)
            {
                inbuffer[s] = 0;
                std::cout << prefix << "# " << inbuffer << std::endl;
                std::string echo = inbuffer;
                echo += "[tcp server echo message]";
                write(sockfd, echo.c_str(), echo.size());
            }
            else
            {
                std::cout << prefix << " client quit" << std::endl;
                break;
            }
        }
    }
    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 4. 获取连接
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = accept(_listensock, CONV(&peer), &len);
            if (sockfd < 0)
            {
                continue;
            }
            ProcessConnection(sockfd, peer);
        }
    }
    ~TcpServer()
    {
    }

private:
    uint16_t _port;
    int _listensock; // TODO
    bool _isrunning;
};

using namespace std;

void Usage(std::string proc)
{
    std::cout << "Usage : \n\t" << proc << " local_port\n"
              << std::endl;
}
// ./tcp_server 8888
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return Usage_Err;
    }
    uint16_t port = stoi(argv[1]);
    std::unique_ptr<TcpServer> tsvr = make_unique<TcpServer>(port);
    tsvr->Init();
    tsvr->Start();

    return 0;
}

// #include <iostream>
// #include <string>
// #include <memory>
// #include <cerrno>
// #include <cstring>
// #include <unistd.h>
// #include <strings.h>
// #include <sys/types.h>
// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <arpa/inet.h>

// const static uint16_t defaultport = 8888;
// const static int defaultfd = -1;
// const static int defaultsize = 1024;

// enum
// {
//     Usage_Err = 1,
//     Socket_Err,
//     Bind_Err
// };

// class UdpServer
// {
// public:
//     UdpServer(uint16_t port = defaultport)
//         : _port(port), _sockfd(defaultfd)
//     {
//     }
//     void Init()
//     {
//         // 1. 创建socket，就是创建了文件细节
//         _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
//         if (_sockfd < 0)
//         {
//             exit(Socket_Err);
//         }

//         // 2. 绑定，指定网络信息
//         struct sockaddr_in local;
//         bzero(&local, sizeof(local)); // memset
//         local.sin_family = AF_INET;
//         local.sin_port = htons(_port);
//         local.sin_addr.s_addr = INADDR_ANY; // 1. 4字节IP 2. 变成网络序列

//         // 结构体填完，设置到内核中了吗？？没有
//         int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local));
//         if (n != 0)
//         {
//             exit(Bind_Err);
//         }
//     }
//     void Start()
//     {
//         // 服务器永远不退出
//         char buffer[defaultsize];
//         for (;;)
//         {
//             struct sockaddr_in peer;
//             socklen_t len = sizeof(peer); // 不能乱写
//             ssize_t n = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
//             if (n > 0)
//             {
//                 uint16_t clientport = ntohs(peer.sin_port);
//                 std::string clientip = inet_ntoa(peer.sin_addr);
//                 std::string prefix = clientip + ":" + std::to_string(clientport);
//                 buffer[n] = 0;
//                 std::cout << prefix << "# " << buffer << std::endl;


//                 std::string echo = buffer;
//                 echo += "[udp server echo message]";
//                 sendto(_sockfd, echo.c_str(), echo.size(), 0, (struct sockaddr *)&peer, len);
//             }
//         }
//     }
//     ~UdpServer()
//     {
//     }

// private:
//     uint16_t _port;
//     int _sockfd;
// };

// void Usage(std::string proc)
// {
//     std::cout << "Usage : \n\t" << proc << " local_port\n"
//               << std::endl;
// }

// // ./udp_server 8888
// int main(int argc, char *argv[])
// {
//     if (argc != 2)
//     {
//         Usage(argv[0]);
//         return Usage_Err;
//     }

//     uint16_t port = std::stoi(argv[1]);
//     std::unique_ptr<UdpServer> usvr = std::make_unique<UdpServer>(port);
//     usvr->Init();
//     usvr->Start();

//     return 0;
// }

// #include <iostream>
// #include <stdio.h>
// #include <unistd.h>
// #include <string.h>
// #include <sys/socket.h>
// #include <netinet/in.h>
// #include <arpa/inet.h>

// int main()
// {
//     int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//     if (listen_sock < 0)
//     {
//         perror("listen sock");
//         return 0;
//     }

//     struct sockaddr_in addr;
//     addr.sin_family = AF_INET;
//     addr.sin_port = htons(8888);
//     addr.sin_addr.s_addr = INADDR_ANY;
//     int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));
//     if (ret < 0)
//     {
//         perror("bind");
//         return 0;
//     }
    
//     ret = listen(listen_sock, 1);
//     if (ret < 0)
//     {
//         perror("listen");
//         return 0;
//     }
    
//     while (true)
//     {
//         struct sockaddr_in peer_addr;
//         socklen_t peer_addrlen = sizeof(peer_addr);
//         int new_sock = accept(listen_sock, (struct sockaddr*)&peer_addr, &peer_addrlen);
//         if (new_sock < 0)
//         {
//             perror("accept");
//             return 0;
//         }
//         printf("accept %s:%d, create newsock %d\n", \
//             inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port), new_sock);
        
//         char buf[1024] = {0};
//         int recv_size = recv(new_sock, buf, sizeof(buf) - 1, 0);
//         if (recv_size > 0)
//         {
//             buf[recv_size] = 0;
//             std::cerr << buf << std::endl;
//         }    
//         else
//         {
//             std::cerr << "recvfrom error" << std::endl;
//             break;
//         }  
//     }
    

//     return 0;
// }

